package pers.cz.easyexcel.analysis;

import org.apache.poi.ss.usermodel.*;
import pers.cz.common.annotation.Column;
import pers.cz.common.annotation.Table;
import pers.cz.common.exception.ErrorCode;
import pers.cz.common.exception.OrmException;
import pers.cz.common.meta.MetaHolder;
import pers.cz.common.meta.TableMeta;
import pers.cz.common.pub.AbstractEntity;
import pers.cz.common.pub.Condition;
import pers.cz.common.pub.Operator;
import pers.cz.easyexcel.meta.ColumnInfo;
import pers.cz.easyexcel.meta.TitleInfo;
import pers.cz.easyexcel.tools.PoiUtils;
import utils.ObjUtils;
import utils.ReflectUtils;
import utils.StringUtils;
import utils.UnsafeUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;


/**
 * @program: Jef-orm
 * @description: excel解析实现
 * @author: Cheng Zhi
 * @create: 2023-09-04 14:36
 **/
public class XlsxAnalysiser implements Analysiser{

    /**
     * 表的元数据信息，包含表名以及字段映射值
     */
    TableMeta tableMeta;

    AbstractEntity entity;

    Workbook workbook;

    public XlsxAnalysiser(AbstractEntity entity, Workbook workbook) {
        this.tableMeta = MetaHolder.initTable(entity);
        this.entity = entity;
        this.workbook = workbook;
    }

    /**
     * 将workbook解析
     * @return
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    public List<AbstractEntity> analysis() throws InstantiationException, IllegalAccessException {

        List<AbstractEntity> entities = new LinkedList<AbstractEntity>();
        List<Sheet> sheets = getSheets();
        for (Sheet sheet : sheets) {
            // 1、解析标题
            Row row = sheet.getRow(0);
            List<TitleInfo> sheetTitle = getSheetTitle(row);
            int lastRowNum = sheet.getLastRowNum();
            for (int i=1; i<=lastRowNum; i++) {
                // 2、解析内容
                List<ColumnInfo> sheetColumnInfo = getSheetColumnInfo(sheet.getRow(i), sheetTitle);
                // 3、组装为对象
                if (sheetColumnInfo != null && sheetColumnInfo.size() > 0) {
                    AbstractEntity entity = sheetToEntity(sheetTitle, sheetColumnInfo);
                    entities.add(entity);
                }

            }
        }

        return entities;
    }

    /**
     * 将sheet页映射到实体
     * @param sheetTitle
     * @param sheetColumnInfo
     * @return
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    private AbstractEntity sheetToEntity(List<TitleInfo> sheetTitle, List<ColumnInfo> sheetColumnInfo) throws IllegalAccessException, InstantiationException {
        AbstractEntity abstractEntity = entity.getClass().newInstance();
        // 一行为一个对象,一列必须是对齐的，否则会出现少解析字段的问题，如果某一列没有映射到对象，则不解析
        for (int i=0; i<sheetTitle.size(); i++) {

            TitleInfo titleInfo = sheetTitle.get(i);
            ColumnInfo columnInfo = sheetColumnInfo.get(i);

            orgEntity(abstractEntity, titleInfo, columnInfo);
        }

        return abstractEntity;
    }

    /**
     * 将字段映射到实体
     * @param abstractEntity
     * @param titleInfo
     * @param columnInfo
     * @throws IllegalAccessException
     */
    private void orgEntity(AbstractEntity abstractEntity,TitleInfo titleInfo, ColumnInfo columnInfo) throws IllegalAccessException {

        // 这里获取到的name是标题行的汉字
        String titleName = titleInfo.getTitleName();

        Map<String, Column> columns = tableMeta.getColumns();
        Class<? extends AbstractEntity> aClass = abstractEntity.getClass();
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field field : declaredFields) {

            if (columns.get(field.getName()).excelColumn().equals(titleName)) {
                long offset = UnsafeUtils.getFiledSet(field);
                UnsafeUtils.forcedSetValue(abstractEntity, offset, ReflectUtils.typeTransform(field, columnInfo.getValue()));
            }
        }
    }

    /**
     * 获取sheet页标题行
     * @param row
     * @return
     */
    public List<TitleInfo> getSheetTitle(Row row) {

        short lastCellNum = row.getLastCellNum(); // 获取到总列数

        List<TitleInfo> listTitles = new ArrayList<>();
        for (short i=0; i<lastCellNum; i++) {
            Cell cell = row.getCell(i);
            TitleInfo titleInfo = new TitleInfo();
            titleInfo.setCellStyle(cell.getCellStyle());
            titleInfo.setTitleName(cell.getRichStringCellValue().getString());
            titleInfo.setNumber(i);
            // 获取到标题映射的字段名
            Map<String, Column> columns = tableMeta.getColumns();

            for (String key : columns.keySet()) {
                if (columns.get(key).excelColumn().equals(titleInfo.getTitleName())) {
                    titleInfo.setFieldName(key);
                    break;
                }
            }

            listTitles.add(titleInfo);
        }

        return listTitles;
    }

    private List<Sheet> getSheets() {
        Table table = getTable();
        String sheetName = table.sheetName();

        List<Sheet> sheetList = new ArrayList<>();
        Sheet sheet = workbook.getSheet(sheetName);
        if (sheet != null) {
            sheetList.add(sheet);
        }
        return sheetList;
    }

    public List<ColumnInfo> getSheetColumnInfo(Row row, List<TitleInfo> sheetTitle) {

        if (row == null) {
            return null;
        }
        List<ColumnInfo> columnInfos = new LinkedList<>();

        List<Boolean> hitLists = new ArrayList<>();
        short lastCellNum = row.getLastCellNum();
        for (short i=0; i<lastCellNum; i++) {
            boolean isHit = false; // 命中标识
            Cell cell = row.getCell(i);
            ColumnInfo columnInfo = new ColumnInfo();
            columnInfo.setType(cell.getCellType());
            columnInfo.setValue(PoiUtils.getCellValue(cell));
            columnInfo.setRowNumber(row.getRowNum());

            List<Condition> conditions = entity.getQuery().getConditions();
            // 如果没有条件则返回所有内容
            if (conditions == null || conditions.size() == 0) {
                isHit = true;
                hitLists.add(isHit);
                columnInfos.add(columnInfo);
                continue;
            }

            for (Condition condition : conditions) {
                Operator operator = condition.getOperator();

                for (TitleInfo titleInfo : sheetTitle) {
                    if (i != titleInfo.getNumber()) {
                        continue;
                    }
                    // 条件字段和excel标题字段映射不上则没必要向下比对
                    if (!titleInfo.getFieldName().equals(ObjUtils.under2camel(condition.getColumnName()))) {
                        continue;
                    }

                    String cellValue = PoiUtils.getCellValue(cell);

                    switch (operator) {
                        case IN: // 值包含
                            List<Object> bindVal = (List<Object>) condition.getBindVal();
                            if (bindVal.contains(cellValue)) {
                                isHit = true;
                            }
                            break;
                        case DEFAULT: // 值相等
                            if (cellValue.equals(condition.getBindVal().toString())) {
                                isHit = true;
                            }
                            break;
                        case OR:
                            if (cellValue.equals(condition.getBindVal().toString())) {
                                isHit = true;
                            }
                            break;
                        case GREAT_EQUALS:
                            if (Long.valueOf(StringUtils.substringBefore(cellValue,".")) >= Long.valueOf(condition.getBindVal().toString())) {
                                isHit = true;
                            }
                            break;
                        case LESS_EQUALS:
                            if (Long.valueOf(StringUtils.substringBefore(cellValue,".")) <= Long.valueOf(condition.getBindVal().toString())) {
                                isHit = true;
                            }
                            break;
                        case GREAT:
                            if (Long.valueOf(StringUtils.substringBefore(cellValue,".")) > Long.valueOf(condition.getBindVal().toString())) {
                                isHit = true;
                            }
                            break;
                        case LESS:
                            if (Long.valueOf(StringUtils.substringBefore(cellValue,".")) < Long.valueOf(condition.getBindVal().toString())) {
                                isHit = true;
                            }
                            break;
                        default:
                            throw new OrmException(ErrorCode.ORM_EXCEL_NOT_SUPPORTED_ERROR);
                    }

                    // 如果命中记录下列号
                    if (isHit) {
                        columnInfo.setColumnNumber(titleInfo.getNumber());
                    }
                    hitLists.add(isHit);

                }

            }
            columnInfos.add(columnInfo);
        }

        // 如果未命中数据行，则清空集合
        if (hitLists.size() == 0 || hitLists.contains(false)) {
            columnInfos.clear();
        }
        return columnInfos;
    }

    @Override
    public List<TitleInfo> analysisTitle() {

        return null;
    }

    @Override
    public List<ColumnInfo> analysisColumn() {
        return null;
    }

    @Override
    public Table getTable() {
        return tableMeta.getTable();
    }

}
